package com.dc.listener;

import org.testng.*;
import org.testng.collections.Lists;
import org.testng.collections.Maps;
import org.testng.collections.Sets;
import org.testng.internal.*;
import org.testng.reporters.FailedReporter;
import org.testng.xml.XmlClass;
import org.testng.xml.XmlInclude;
import org.testng.xml.XmlSuite;
import org.testng.xml.XmlTest;

import java.util.*;
import java.util.stream.Collectors;


public class MyFailedReporter extends FailedReporter {
    public static final String TESTNG_FAILED_XML = "testng-failed.xml";

    private XmlSuite m_xmlSuite;

    public MyFailedReporter() {
    }

    public MyFailedReporter(XmlSuite xmlSuite) {
        m_xmlSuite = xmlSuite;
    }

    public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
        Iterator var4 = suites.iterator();

        while(var4.hasNext()) {
            ISuite suite = (ISuite)var4.next();
            this.generateFailureSuite(suite.getXmlSuite(), suite, outputDirectory);
        }

    }

    protected void generateFailureSuite(XmlSuite xmlSuite, ISuite suite, String outputDir) {
        XmlSuite failedSuite = xmlSuite.shallowCopy();
        failedSuite.setName("Failed suite [" + xmlSuite.getName() + "]");
        m_xmlSuite = failedSuite;

        Map<String, ISuiteResult> results = suite.getResults();

        for (Map.Entry<String, ISuiteResult> entry : results.entrySet()) {
            ISuiteResult suiteResult = entry.getValue();
            ITestContext testContext = suiteResult.getTestContext();

            generateXmlTest(testContext.getCurrentXmlTest(),
                    testContext,
                    testContext.getFailedTests().getAllResults(),
                    testContext.getSkippedTests().getAllResults());
        }
        
        if (null != failedSuite.getTests() && failedSuite.getTests().size() > 0) {
            Utils.writeUtf8File(outputDir, TESTNG_FAILED_XML, failedSuite.toXml());
//            注释原FailedReporter类该行代码，可减少生成测试套件文件夹的testng-failed.xml文件
//            Utils.writeUtf8File(suite.getOutputDirectory(),TESTNG_FAILED_XML, failedSuite.toXml());
        }
    }

    private void generateXmlTest(XmlTest xmlTest, ITestContext context, Set<ITestResult> failedTests, Set<ITestResult> skippedTests) {
        if (skippedTests.size() > 0 || failedTests.size() > 0) {
            Set<ITestNGMethod> methodsToReRun = Sets.newHashSet();
            Set<ITestResult> allTests = Sets.newHashSet();
            allTests.addAll(failedTests);
            allTests.addAll(skippedTests);
            ITestNGMethod[] allTestMethods = context.getAllTestMethods();
            Iterator var8 = allTests.iterator();

            while(true) {
                ITestNGMethod current;
                do {
                    if (!var8.hasNext()) {
                        List<ITestNGMethod> result = Lists.newArrayList();
                        Set<ITestNGMethod> relevantConfigs = Sets.newHashSet();
                        ITestNGMethod[] var16 = allTestMethods;
                        int var17 = allTestMethods.length;

                        for(int var18 = 0; var18 < var17; ++var18) {
                            ITestNGMethod m = var16[var18];
                            if (RuntimeBehavior.isMemoryFriendlyMode()) {
                                m = new LiteWeightTestNGMethod((ITestNGMethod)m);
                            }

                            if (methodsToReRun.contains(m)) {
                                result.add(m);
                                getAllApplicableConfigs(relevantConfigs, ((ITestNGMethod)m).getTestClass());
                            }
                        }

                        result.addAll(relevantConfigs);
                        this.createXmlTest(context, result, xmlTest);
                        return;
                    }

                    ITestResult failedTest = (ITestResult)var8.next();
                    current = failedTest.getMethod();
                } while(!current.isTest());

                methodsToReRun.add(current);
                List<ITestNGMethod> methodsDependedUpon = MethodHelper.getMethodsDependedUpon(current, allTestMethods, Systematiser.getComparator());
                Iterator var12 = methodsDependedUpon.iterator();

                while(var12.hasNext()) {
                    ITestNGMethod m = (ITestNGMethod)var12.next();
                    if (m.isTest()) {
                        methodsToReRun.add(m);
                    }
                }
            }
        }
    }

    private static void getAllApplicableConfigs(Set<ITestNGMethod> configs, ITestClass iTestClass) {
        configs.addAll(Arrays.asList(iTestClass.getBeforeSuiteMethods()));
        configs.addAll(Arrays.asList(iTestClass.getAfterSuiteMethods()));
        configs.addAll(Arrays.asList(iTestClass.getBeforeTestConfigurationMethods()));
        configs.addAll(Arrays.asList(iTestClass.getAfterTestConfigurationMethods()));
        configs.addAll(Arrays.asList(iTestClass.getBeforeTestMethods()));
        configs.addAll(Arrays.asList(iTestClass.getAfterTestMethods()));
        configs.addAll(Arrays.asList(iTestClass.getBeforeClassMethods()));
        configs.addAll(Arrays.asList(iTestClass.getAfterClassMethods()));
    }

    private void createXmlTest(ITestContext context, List<ITestNGMethod> methods, XmlTest srcXmlTest) {
        XmlTest xmlTest = new XmlTest(this.m_xmlSuite);
        xmlTest.setName(context.getName() + "(failed)");
        xmlTest.setScript(srcXmlTest.getScript());
        xmlTest.setIncludedGroups(srcXmlTest.getIncludedGroups());
        xmlTest.setExcludedGroups(srcXmlTest.getExcludedGroups());
        xmlTest.setParallel(srcXmlTest.getParallel());
        xmlTest.setParameters(srcXmlTest.getLocalParameters());
        xmlTest.setJUnit(srcXmlTest.isJUnit());
        List<XmlClass> xmlClasses = this.createXmlClasses(methods, srcXmlTest);
        xmlTest.setXmlClasses(xmlClasses);
    }

    private List<XmlClass> createXmlClasses(List<ITestNGMethod> methods, XmlTest srcXmlTest) {
        List<XmlClass> result = Lists.newArrayList();
        Map<Class<?>, Set<ITestNGMethod>> methodsMap = Maps.newHashMap();
        Iterator var5 = methods.iterator();

        while(var5.hasNext()) {
            ITestNGMethod m = (ITestNGMethod)var5.next();
            Object instances = m.getInstance();
            Class<?> clazz = instances == null ? m.getRealClass() : instances.getClass();
            Set<ITestNGMethod> methodList = (Set)methodsMap.computeIfAbsent(clazz, (k) -> {
                return Sets.newHashSet();
            });
            methodList.add(m);
        }

        Map<String, Map<String, String>> classParameters = Maps.newHashMap();
        Iterator var18 = srcXmlTest.getClasses().iterator();

        while(var18.hasNext()) {
            XmlClass c = (XmlClass)var18.next();
            classParameters.put(c.getName(), c.getLocalParameters());
        }

        int index = 0;
        Iterator var21 = methodsMap.entrySet().iterator();

        while(var21.hasNext()) {
            Map.Entry<Class<?>, Set<ITestNGMethod>> entry = (Map.Entry)var21.next();
            Class<?> clazz = (Class)entry.getKey();
            Set<ITestNGMethod> methodList = (Set)entry.getValue();
            XmlClass xmlClass = new XmlClass(clazz.getName(), index++, false);
            List<XmlInclude> methodNames = Lists.newArrayList(methodList.size());
            int ind = 0;
            Iterator var14 = methodList.iterator();

            while(var14.hasNext()) {
                ITestNGMethod m = (ITestNGMethod)var14.next();
                XmlInclude methodName = new XmlInclude(m.getMethodName(), m.getFailedInvocationNumbers(), ind++);
                methodName.setParameters(findMethodLocalParameters(srcXmlTest, m));
                methodNames.add(methodName);
            }

            xmlClass.setIncludedMethods(methodNames);
            xmlClass.setParameters((Map)classParameters.getOrDefault(xmlClass.getName(), classParameters.values().stream().flatMap((map) -> {
                return map.entrySet().stream();
            }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> {
                return v2;
            }))));
            result.add(xmlClass);
        }

        return result;
    }

    private static Map<String, String> findMethodLocalParameters(XmlTest srcXmlTest, ITestNGMethod method) {
        Class<?> clazz = method.getRealClass();
        Iterator var3 = srcXmlTest.getClasses().iterator();

        while(true) {
            XmlClass c;
            do {
                if (!var3.hasNext()) {
                    return Collections.emptyMap();
                }

                c = (XmlClass)var3.next();
            } while(clazz != c.getSupportClass());

            Iterator var5 = c.getIncludedMethods().iterator();

            while(var5.hasNext()) {
                XmlInclude xmlInclude = (XmlInclude)var5.next();
                if (xmlInclude.getName().equals(method.getMethodName())) {
                    return xmlInclude.getLocalParameters();
                }
            }
        }
    }


}
